package com.qsw.qswcommonlib.util;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.TrafficStats;
import android.os.Debug;
import android.os.Process;
import android.os.SystemClock;

import com.qsw.qswcommonlib.bean.AppMemoryInfo;
import com.qsw.qswcommonlib.bean.TrafficInfo;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * APP运行过程中性能数据获取
 */
public class CommonAppRunning {
    // 获取 时钟频率 的命令
    private static final String[] CLK_CMD = {"sh", "-c", "getconf CLK_TCK"};
    // 存储 CPU 使用信息的文件
    private static final File mPidStatFile = new File("/proc/" + Process.myPid() + "/stat");

    // Linux 默认时钟频率为 100 HZ
    private int CLK_TCK = 100;
    // 上次的CPU使用时间
    private long mLastUsedCPUTime = 0L;
    // 上次的CPU记录时间
    private long mLastRecordCPUTime = SystemClock.uptimeMillis();

    private static CommonAppRunning mInstance;

    private CommonAppRunning() {
    }

    public static CommonAppRunning getInstance() {
        if (mInstance == null) {
            synchronized (CommonAppRunning.class) {
                if (mInstance == null) {
                    mInstance = new CommonAppRunning();
                }
            }
        }
        return mInstance;
    }

    /**
     * 计算方法 ∆(uTime + sTime) / ∆upTimeMillis / 1000 * CLK_TCK / CORE_COUNT
     * 计算方法 进程运行时间片数 / (已过去时间 * 时钟频率 * 可用核心数)
     *
     * @return 当前CPU使用率%(from 0 to 100)
     */
    public float getRunningCpuInfo() throws IOException {
        try (FileInputStream inputStream = new FileInputStream(mPidStatFile);
             InputStreamReader reader = new InputStreamReader(inputStream);
             BufferedReader bufferedReader = new BufferedReader(reader)
        ) {
            String line = bufferedReader.readLine();
            String[] res = line.split(" ");
            // 进程在用户态运行的时间
            long uTime = Long.parseLong(res[13]);
            // 进程在内核态运行的时间
            long sTime = Long.parseLong(res[14]);
            // 本次运行总时间
            long usedTime = (uTime + sTime) - mLastUsedCPUTime;

            long currentTime = SystemClock.uptimeMillis();
            // 获得已过去时间 ms ==> s ==> Clock Tick
            float elapsedTime = (currentTime - mLastRecordCPUTime) / 1000f * CLK_TCK;

            mLastUsedCPUTime = uTime + sTime;
            mLastRecordCPUTime = currentTime;

            float usage = usedTime / elapsedTime * 100;
            return usage / Runtime.getRuntime().availableProcessors();
        }
    }

    /**
     * 确保时钟频率正确，以防厂商定制
     * 测试 坚果1 Android5.1 获取参数失败，但默认也是 100
     */
    private void configClkTck() throws IOException {
        java.lang.Process process = Runtime.getRuntime().exec(CLK_CMD);
        try (InputStream inputStream = process.getInputStream();
             InputStreamReader reader = new InputStreamReader(inputStream);
             BufferedReader bufferedReader = new BufferedReader(reader)) {
            CLK_TCK = Integer.parseInt(bufferedReader.readLine());
        }
        process.destroy();
    }

    /**
     * 获取APP内存使用情况
     *
     * @return APP内存使用情况
     */
    public static AppMemoryInfo getRunningMemoryInfo() {
        Debug.MemoryInfo memoryInfo = new Debug.MemoryInfo();
        Debug.getMemoryInfo(memoryInfo);
        // 读取内存信息,跟Android Profiler 分析一致
        AppMemoryInfo appMemoryInfo = new AppMemoryInfo();

        appMemoryInfo.setJavaMemory(memoryInfo.getMemoryStat("summary.java-heap"));
        appMemoryInfo.setNativeMemory(memoryInfo.getMemoryStat("summary.native-heap"));
        appMemoryInfo.setGraphicsMemory(memoryInfo.getMemoryStat("summary.graphics"));
        appMemoryInfo.setStackMemory(memoryInfo.getMemoryStat("summary.stack"));
        appMemoryInfo.setCodeMemory(memoryInfo.getMemoryStat("summary.code"));
        appMemoryInfo.setOthersMemory(memoryInfo.getMemoryStat("summary.private-other"));

        return appMemoryInfo;
    }

    /**
     * 获取系统流量使用情况
     *
     * @param context 环境
     * @param sort    0：不排序；1：按总使用量前高后低；2：按总使用量前低后高
     * @return 系统流量使用情况
     */
    public static List<TrafficInfo> getAppTrafficList(Context context, final int sort) {
        List<TrafficInfo> result = new ArrayList<>();
        // 获取所有的安装在手机上的应用软件的信息，并且获取这些软件里面的权限信息
        PackageManager pm = context.getPackageManager();// 获取系统应用包管理
        // 获取每个包内的androidmanifest.xml信息，它的权限等等
        List<PackageInfo> pinfos = pm.getInstalledPackages(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_PERMISSIONS);
        // 遍历每个应用包信息
        for (PackageInfo info : pinfos) {
            // 请求每个程序包对应的androidManifest.xml里面的权限
            String[] premissions = info.requestedPermissions;
            if (premissions != null && premissions.length > 0) {
                // 找出需要网络服务的应用程序
                for (String premission : premissions) {
                    if ("android.permission.INTERNET".equals(premission)) {
                        // 获取每个应用程序在操作系统内的进程id
                        int uId = info.applicationInfo.uid;
                        // 如果返回-1，代表不支持使用该方法，注意必须是2.2以上的
                        long rx = TrafficStats.getUidRxBytes(uId);
                        // 如果返回-1，代表不支持使用该方法，注意必须是2.2以上的
                        long tx = TrafficStats.getUidTxBytes(uId);
                        if (rx <= 0 && tx <= 0) {
                            break;
                        } else {
                            result.add(new TrafficInfo(uId, info.packageName, rx, tx));
                        }
                    }
                }
            }
        }
        if (sort == 1 || sort == 2) {
            Collections.sort(result, new Comparator<TrafficInfo>() {
                @Override
                public int compare(TrafficInfo o1, TrafficInfo o2) {
                    if (sort == 1) {
                        return (o1.getRx() + o1.getTx()) > (o2.getRx() + o2.getTx()) ? -1 : 1;
                    } else {
                        return (o1.getRx() + o1.getTx()) > (o2.getRx() + o2.getTx()) ? 1 : -1;
                    }
                }
            });
        }

        return result;
    }
}
